/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.fellowera.framework;

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

/**
 *
 * @author oyku
 */
public abstract class EntityQuery<T extends BaseEntity> {

    // query
    protected String ejbql;
    protected String ejbqlCount;
    // results
    private List<T> resultList;
    private T singleResult;
    // paging
    private Integer firstResult;
    private Integer maxResults;
    
    @PersistenceContext
    private EntityManager em;
    private Long resultCount;

    public EntityQuery() {
    }

    // ************************************************************************************************************************
    public EntityManager getEntityManager() {
        return em;
    }

    // ************************************************************************************************************************
    // Result lists
    public List<T> getResultList() {
        if (isAnyParameterDirty()) {
            refresh();
        }
        initResultList();
        return truncResultList(resultList);
    }

    public T getSingleResult() {
        if (isAnyParameterDirty()) {
            refresh();
        }
        initSingleResult();
        return singleResult;
    }

    public Long getResultCount() {
        if (isAnyParameterDirty()) {
            refresh();
        }
        initResultCount();
        return resultCount;
    }

    // ************************************************************************************************************************
    // getters/setters
    public Integer getFirstResult() {
        return firstResult;
    }

    public void setFirstResult(Integer firstResult) {
        this.firstResult = firstResult;
        refresh();
    }

    public Integer getMaxResults() {
        return maxResults;
    }

    public void setMaxResults(Integer maxResults) {
        this.maxResults = maxResults;
        refresh();
    }

    // ************************************************************************************************************************
    // Pagination
    public boolean isNextExists() {
        return resultList != null && getMaxResults() != null && resultList.size() > getMaxResults();
        //return getPageCount() > 0;
    }

    public boolean isPreviousExists() {
        return getFirstResult() != null && getFirstResult() != 0;
    }

    public boolean isPaginated() {
        return isNextExists() || isPreviousExists();
    }

    public void first() {
        setFirstResult(0);
    }

    public void previous() {
        setFirstResult(getPreviousFirstResult());
    }

    public void next() {
        setFirstResult(getNextFirstResult());
    }

    public void last() {
        setFirstResult(getLastFirstResult().intValue());
    }

    // ************************************************************************************************************************
    // Paginator value assignments
    public Integer getPageCount() {
        if (getMaxResults() == null) {
            return null;
        } else {
            int rc = getResultCount().intValue();
            int mr = getMaxResults().intValue();
            int pages = rc / mr;
            return rc % mr == 0 ? pages : pages + 1;
        }
    }

    public Long getLastFirstResult() {
        Integer pc = getPageCount();
        return pc == null ? null : (pc.longValue() - 1) * getMaxResults();
    }

    public int getPreviousFirstResult() {
        return getMaxResults() >= (getFirstResult() == null ? 0 : getFirstResult()) ? 0 : getFirstResult() - getMaxResults();
    }

    public int getNextFirstResult() {
        return (getFirstResult() == null ? 0 : getFirstResult()) + getMaxResults();
    }

    public void refresh() {
        resultCount = null;
        resultList = null;
        singleResult = null;
    }

    // ************************************************************************************************************************
    private static boolean isAnyParameterDirty() {
        return false;
    }

    @SuppressWarnings("unchecked")
	private void initResultList() {
        if (resultList == null) {
            Query q = em.createQuery(ejbql);
            if (getFirstResult() != null) {
                q.setFirstResult(getFirstResult());
            }
            if (getMaxResults() != null) {
                q.setMaxResults(getMaxResults() + 1);
            }
            resultList = q == null ? null : q.getResultList();
        }
    }

    @SuppressWarnings("unchecked")
	private void initSingleResult() {
        if (singleResult == null) {
            Query q = em.createQuery(ejbql);
            singleResult = (T) (q == null ? null : q.getSingleResult());
        }
    }

    private void initResultCount() {
        if (resultCount == null) {
            Query q = em.createQuery(ejbqlCount);
            resultCount = (Long) (q == null ? null : q.getSingleResult());
        }
    }

    private List<T> truncResultList(List<T> results) {
        Integer mr = getMaxResults();
        if (mr != null && results.size() > mr) {
            return results.subList(0, mr);
        } else {
            return results;
        }

    }
}
